home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 2 / AACD 2.iso / AACD / Magazine / GraphicsCards / StormMesa / src-glut / glut_input.c < prev    next >
C/C++ Source or Header  |  1998-12-15  |  19KB  |  631 lines

  1.  
  2. /* Copyright (c) Mark J. Kilgard, 1994, 1997, 1998. */
  3.  
  4. /* This program is freely distributable without licensing fees
  5.    and is provided without guarantee or warrantee expressed or
  6.    implied. This program is -not- in the public domain. */
  7.  
  8. #include <assert.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12.  
  13. #if !defined(_WIN32)
  14. #include <X11/Xlib.h>
  15. #if defined(__vms)
  16. #include <X11/XInput.h>
  17. #else
  18. #include <X11/extensions/XInput.h>
  19. #endif
  20. #include <X11/Xutil.h>
  21. #else
  22. #include <windows.h>
  23. #include <mmsystem.h>  /* Win32 Multimedia API header. */
  24. #endif /* !_WIN32 */
  25.  
  26. #include "glutint.h"
  27.  
  28. int __glutNumDials = 0;
  29. int __glutNumSpaceballButtons = 0;
  30. int __glutNumButtonBoxButtons = 0;
  31. int __glutNumTabletButtons = 0;
  32. int __glutNumMouseButtons = 3;  /* Good guess. */
  33. XDevice *__glutTablet = NULL;
  34. XDevice *__glutDials = NULL;
  35. XDevice *__glutSpaceball = NULL;
  36.  
  37. int __glutHasJoystick = 0;
  38. int __glutNumJoystickButtons = 0;
  39. int __glutNumJoystickAxes = 0;
  40.  
  41. #if !defined(_WIN32)
  42. typedef struct _Range {
  43.   int min;
  44.   int range;
  45. } Range;
  46.  
  47. #define NUM_SPACEBALL_AXIS    6
  48. #define NUM_TABLET_AXIS        2
  49. #define NUM_DIALS_AXIS        8
  50.  
  51. Range __glutSpaceballRange[NUM_SPACEBALL_AXIS];
  52. Range __glutTabletRange[NUM_TABLET_AXIS];
  53. int *__glutDialsResolution;
  54.  
  55. /* Safely assumes 0 is an illegal event type for X Input
  56.    extension events. */
  57. int __glutDeviceMotionNotify = 0;
  58. int __glutDeviceButtonPress = 0;
  59. int __glutDeviceButtonPressGrab = 0;
  60. int __glutDeviceButtonRelease = 0;
  61. int __glutDeviceStateNotify = 0;
  62.  
  63. static int
  64. normalizeTabletPos(int axis, int rawValue)
  65. {
  66.   assert(rawValue >= __glutTabletRange[axis].min);
  67.   assert(rawValue <= __glutTabletRange[axis].min
  68.     + __glutTabletRange[axis].range);
  69.   /* Normalize rawValue to between 0 and 4000. */
  70.   return ((rawValue - __glutTabletRange[axis].min) * 4000) /
  71.     __glutTabletRange[axis].range;
  72. }
  73.  
  74. static int
  75. normalizeDialAngle(int axis, int rawValue)
  76. {
  77.   /* XXX Assumption made that the resolution of the device is
  78.      number of clicks for one complete dial revolution.  This
  79.      is true for SGI's dial & button box. */
  80.   return (rawValue * 360.0) / __glutDialsResolution[axis];
  81. }
  82.  
  83. static int
  84. normalizeSpaceballAngle(int axis, int rawValue)
  85. {
  86.   assert(rawValue >= __glutSpaceballRange[axis].min);
  87.   assert(rawValue <= __glutSpaceballRange[axis].min +
  88.     __glutSpaceballRange[axis].range);
  89.   /* Normalize rawValue to between -1800 and 1800. */
  90.   return ((rawValue - __glutSpaceballRange[axis].min) * 3600) /
  91.     __glutSpaceballRange[axis].range - 1800;
  92. }
  93.  
  94. static int
  95. normalizeSpaceballDelta(int axis, int rawValue)
  96. {
  97.   assert(rawValue >= __glutSpaceballRange[axis].min);
  98.   assert(rawValue <= __glutSpaceballRange[axis].min +
  99.     __glutSpaceballRange[axis].range);
  100.   /* Normalize rawValue to between -1000 and 1000. */
  101.   return ((rawValue - __glutSpaceballRange[axis].min) * 2000) /
  102.     __glutSpaceballRange[axis].range - 1000;
  103. }
  104.  
  105. static void
  106. queryTabletPos(GLUTwindow * window)
  107. {
  108.   XDeviceState *state;
  109.   XInputClass *any;
  110.   XValuatorState *v;
  111.   int i;
  112.  
  113.   state = XQueryDeviceState(__glutDisplay, __glutTablet);
  114.   any = state->data;
  115.   for (i = 0; i < state->num_classes; i++) {
  116. #if defined(__cplusplus) || defined(c_plusplus)
  117.     switch (any->c_class) {
  118. #else
  119.     switch (any->class) {
  120. #endif
  121.     case ValuatorClass:
  122.       v = (XValuatorState *) any;
  123.       if (v->num_valuators < 2)
  124.         goto end;
  125.       if (window->tabletPos[0] == -1)
  126.         window->tabletPos[0] = normalizeTabletPos(0, v->valuators[0]);
  127.       if (window->tabletPos[1] == -1)
  128.         window->tabletPos[1] = normalizeTabletPos(1, v->valuators[1]);
  129.     }
  130.     any = (XInputClass *) ((char *) any + any->length);
  131.   }
  132. end:
  133.   XFreeDeviceState(state);
  134. }
  135.  
  136. static void
  137. tabletPosChange(GLUTwindow * window, int first, int count, int *data)
  138. {
  139.   int i, value, genEvent = 0;
  140.  
  141.   for (i = first; i < first + count; i++) {
  142.     switch (i) {
  143.     case 0:            /* X axis */
  144.     case 1:            /* Y axis */
  145.       value = normalizeTabletPos(i, data[i - first]);
  146.       if (value != window->tabletPos[i]) {
  147.         window->tabletPos[i] = value;
  148.         genEvent = 1;
  149.       }
  150.       break;
  151.     }
  152.   }
  153.   if (window->tabletPos[0] == -1 || window->tabletPos[1] == -1)
  154.     queryTabletPos(window);
  155.   if (genEvent)
  156.     window->tabletMotion(window->tabletPos[0], window->tabletPos[1]);
  157. }
  158. #endif /* !_WIN32 */
  159.  
  160. int
  161. __glutProcessDeviceEvents(XEvent * event)
  162. {
  163. #if !defined(_WIN32)
  164.   GLUTwindow *window;
  165.  
  166.   /* XXX Ugly code fan out. */
  167.  
  168.   /* Can't use switch/case since X Input event types are
  169.      dynamic. */
  170.  
  171.   if (__glutDeviceMotionNotify && event->type == __glutDeviceMotionNotify) {
  172.     XDeviceMotionEvent *devmot = (XDeviceMotionEvent *) event;
  173.  
  174.     window = __glutGetWindow(devmot->window);
  175.     if (window) {
  176.       if (__glutTablet
  177.         && devmot->deviceid == __glutTablet->device_id
  178.         && window->tabletMotion) {
  179.         tabletPosChange(window, devmot->first_axis, devmot->axes_count,
  180.           devmot->axis_data);
  181.       } else if (__glutDials
  182.           && devmot->deviceid == __glutDials->device_id
  183.         && window->dials) {
  184.         int i, first = devmot->first_axis, count = devmot->axes_count;
  185.  
  186.         for (i = first; i < first + count; i++)
  187.           window->dials(i + 1,
  188.             normalizeDialAngle(i, devmot->axis_data[i - first]));
  189.       } else if (__glutSpaceball
  190.         && devmot->deviceid == __glutSpaceball->device_id) {
  191.         /* XXX Assume that space ball motion events come in as
  192.            all the first 6 axes.  Assume first 3 axes are XYZ
  193.            translations; second 3 axes are XYZ rotations. */
  194.         if (devmot->first_axis == 0 && devmot->axes_count == 6) {
  195.           if (window->spaceMotion)
  196.             window->spaceMotion(
  197.               normalizeSpaceballDelta(0, devmot->axis_data[0]),
  198.               normalizeSpaceballDelta(1, devmot->axis_data[1]),
  199.               normalizeSpaceballDelta(2, devmot->axis_data[2]));
  200.           if (window->spaceRotate)
  201.             window->spaceRotate(
  202.               normalizeSpaceballAngle(3, devmot->axis_data[3]),
  203.               normalizeSpaceballAngle(4, devmot->axis_data[4]),
  204.               normalizeSpaceballAngle(5, devmot->axis_data[5]));
  205.         }
  206.       }
  207.       return 1;
  208.     }
  209.   } else if (__glutDeviceButtonPress
  210.     && event->type == __glutDeviceButtonPress) {
  211.     XDeviceButtonEvent *devbtn = (XDeviceButtonEvent *) event;
  212.  
  213.     window = __glutGetWindow(devbtn->window);
  214.     if (window) {
  215.       if (__glutTablet
  216.         && devbtn->deviceid == __glutTablet->device_id
  217.         && window->tabletButton
  218.         && devbtn->first_axis == 0
  219.         && devbtn->axes_count == 2) {
  220.         tabletPosChange(window, devbtn->first_axis, devbtn->axes_count,
  221.           devbtn->axis_data);
  222.         window->tabletButton(devbtn->button, GLUT_DOWN,
  223.           window->tabletPos[0], window->tabletPos[1]);
  224.       } else if (__glutDials
  225.           && devbtn->deviceid == __glutDials->device_id
  226.         && window->buttonBox) {
  227.         window->buttonBox(devbtn->button, GLUT_DOWN);
  228.       } else if (__glutSpaceball
  229.           && devbtn->deviceid == __glutSpaceball->device_id
  230.         && window->spaceButton) {
  231.         window->spaceButton(devbtn->button, GLUT_DOWN);
  232.       }
  233.       return 1;
  234.     }
  235.   } else if (__glutDeviceButtonRelease
  236.     && event->type == __glutDeviceButtonRelease) {
  237.     XDeviceButtonEvent *devbtn = (XDeviceButtonEvent *) event;
  238.  
  239.     window = __glutGetWindow(devbtn->window);
  240.     if (window) {
  241.       if (__glutTablet
  242.         && devbtn->deviceid == __glutTablet->device_id
  243.         && window->tabletButton
  244.         && devbtn->first_axis == 0
  245.         && devbtn->axes_count == 2) {
  246.         tabletPosChange(window, devbtn->first_axis, devbtn->axes_count,
  247.           devbtn->axis_data);
  248.         window->tabletButton(devbtn->button, GLUT_UP,
  249.           window->tabletPos[0], window->tabletPos[1]);
  250.       } else if (__glutDials
  251.           && devbtn->deviceid == __glutDials->device_id
  252.         && window->buttonBox) {
  253.         window->buttonBox(devbtn->button, GLUT_UP);
  254.       } else if (__glutSpaceball
  255.           && devbtn->deviceid == __glutSpaceball->device_id
  256.         && window->spaceButton) {
  257.         window->spaceButton(devbtn->button, GLUT_UP);
  258.       }
  259.       return 1;
  260.     }
  261.   }
  262. #else
  263.   {
  264.     JOYINFOEX info;
  265.     int njoyId = 0;
  266.     int nConnected = 0;
  267.     MMRESULT result;
  268.  
  269.     /* Loop through all possible joystick IDs until we get the error
  270.        JOYERR_PARMS. Count the number of times we get JOYERR_NOERROR
  271.        indicating an installed joystick driver with a joystick currently
  272.        attached to the port. */
  273.     while ((result = joyGetPosEx(njoyId++,&info)) != JOYERR_PARMS) {
  274.       if (result == JOYERR_NOERROR) {
  275.         ++nConnected;  /* The count of connected joysticks. */
  276.       }
  277.     }
  278.   }
  279. #endif /* !_WIN32 */
  280.   return 0;
  281. }
  282.  
  283. static GLUTeventParser eventParser =
  284. {__glutProcessDeviceEvents, NULL};
  285.  
  286. static void
  287. addDeviceEventParser(void)
  288. {
  289.   static Bool been_here = False;
  290.  
  291.   if (been_here)
  292.     return;
  293.   been_here = True;
  294.   __glutRegisterEventParser(&eventParser);
  295. }
  296.  
  297. static int
  298. probeDevices(void)
  299. {
  300.   static Bool been_here = False;
  301.   static int support;
  302. #if !defined(_WIN32)
  303.   XExtensionVersion *version;
  304.   XDeviceInfoPtr device_info, device;
  305.   XAnyClassPtr any;
  306.   XButtonInfoPtr b;
  307.   XValuatorInfoPtr v;
  308.   XAxisInfoPtr a;
  309.   int num_dev, btns, dials;
  310.   int i, j, k;
  311. #endif /* !_WIN32 */
  312.  
  313.   if (been_here) {
  314.     return support;
  315.   }
  316.   been_here = True;
  317.  
  318. #if !defined(_WIN32)
  319.   version = XGetExtensionVersion(__glutDisplay, "XInputExtension");
  320.   /* Ugh.  XInput extension API forces annoying cast of a pointer
  321.      to a long so it can be compared with the NoSuchExtension
  322.      value (#defined to 1). */
  323.   if (version == NULL || ((long) version) == NoSuchExtension) {
  324.     support = 0;
  325.     return support;
  326.   }
  327.   XFree(version);
  328.   device_info = XListInputDevices(__glutDisplay, &num_dev);
  329.   if (device_info) {
  330.     for (i = 0; i < num_dev; i++) {
  331.       /* XXX These are SGI names for these devices;
  332.          unfortunately, no good standard exists for standard
  333.          types of X input extension devices. */
  334.  
  335.       device = &device_info[i];
  336.       any = (XAnyClassPtr) device->inputclassinfo;
  337.  
  338.       if (!__glutSpaceball && !strcmp(device->name, "spaceball")) {
  339.         v = NULL;
  340.         b = NULL;
  341.         for (j = 0; j < device->num_classes; j++) {
  342. #if defined(__cplusplus) || defined(c_plusplus)
  343.           switch (any->c_class) {
  344. #else
  345.           switch (any->class) {
  346. #endif
  347.           case ButtonClass:
  348.             b = (XButtonInfoPtr) any;
  349.             btns = b->num_buttons;
  350.             break;
  351.           case ValuatorClass:
  352.             v = (XValuatorInfoPtr) any;
  353.             /* Sanity check: at least 6 valuators? */
  354.             if (v->num_axes < NUM_SPACEBALL_AXIS)
  355.               goto skip_device;
  356.             a = (XAxisInfoPtr) ((char *) v + sizeof(XValuatorInfo));
  357.             for (k = 0; k < NUM_SPACEBALL_AXIS; k++, a++) {
  358.               __glutSpaceballRange[k].min = a->min_value;
  359.               __glutSpaceballRange[k].range = a->max_value - a->min_value;
  360.             }
  361.             break;
  362.           }
  363.           any = (XAnyClassPtr) ((char *) any + any->length);
  364.         }
  365.         if (v) {
  366.           __glutSpaceball = XOpenDevice(__glutDisplay, device->id);
  367.           if (__glutSpaceball) {
  368.             __glutNumSpaceballButtons = btns;
  369.             addDeviceEventParser();
  370.           }
  371.         }
  372.       } else if (!__glutDials && !strcmp(device->name, "dial+buttons")) {
  373.         v = NULL;
  374.         b = NULL;
  375.         for (j = 0; j < device->num_classes; j++) {
  376. #if defined(__cplusplus) || defined(c_plusplus)
  377.           switch (any->c_class) {
  378. #else
  379.           switch (any->class) {
  380. #endif
  381.           case ButtonClass:
  382.             b = (XButtonInfoPtr) any;
  383.             btns = b->num_buttons;
  384.             break;
  385.           case ValuatorClass:
  386.             v = (XValuatorInfoPtr) any;
  387.             /* Sanity check: at least 8 valuators? */
  388.             if (v->num_axes < NUM_DIALS_AXIS)
  389.               goto skip_device;
  390.             dials = v->num_axes;
  391.             __glutDialsResolution = (int *) malloc(sizeof(int) * dials);
  392.             a = (XAxisInfoPtr) ((char *) v + sizeof(XValuatorInfo));
  393.             for (k = 0; k < dials; k++, a++) {
  394.               __glutDialsResolution[k] = a->resolution;
  395.             }
  396.             break;
  397.           }
  398.           any = (XAnyClassPtr) ((char *) any + any->length);
  399.         }
  400.         if (v) {
  401.           __glutDials = XOpenDevice(__glutDisplay, device->id);
  402.           if (__glutDials) {
  403.             __glutNumButtonBoxButtons = btns;
  404.             __glutNumDials = dials;
  405.             addDeviceEventParser();
  406.           }
  407.         }
  408.       } else if (!__glutTablet && !strcmp(device->name, "tablet")) {
  409.         v = NULL;
  410.         b = NULL;
  411.         for (j = 0; j < device->num_classes; j++) {
  412. #if defined(__cplusplus) || defined(c_plusplus)
  413.           switch (any->c_class) {
  414. #else
  415.           switch (any->class) {
  416. #endif
  417.           case ButtonClass:
  418.             b = (XButtonInfoPtr) any;
  419.             btns = b->num_buttons;
  420.             break;
  421.           case ValuatorClass:
  422.             v = (XValuatorInfoPtr) any;
  423.             /* Sanity check: exactly 2 valuators? */
  424.             if (v->num_axes != NUM_TABLET_AXIS)
  425.               goto skip_device;
  426.             a = (XAxisInfoPtr) ((char *) v + sizeof(XValuatorInfo));
  427.             for (k = 0; k < NUM_TABLET_AXIS; k++, a++) {
  428.               __glutTabletRange[k].min = a->min_value;
  429.               __glutTabletRange[k].range = a->max_value - a->min_value;
  430.             }
  431.             break;
  432.           }
  433.           any = (XAnyClassPtr) ((char *) any + any->length);
  434.         }
  435.         if (v) {
  436.           __glutTablet = XOpenDevice(__glutDisplay, device->id);
  437.           if (__glutTablet) {
  438.             __glutNumTabletButtons = btns;
  439.             addDeviceEventParser();
  440.           }
  441.         }
  442.       } else if (!strcmp(device->name, "mouse")) {
  443.         for (j = 0; j < device->num_classes; j++) {
  444. #if defined(__cplusplus) || defined(c_plusplus)
  445.           if (any->c_class == ButtonClass) {
  446. #else
  447.           if (any->class == ButtonClass) {
  448. #endif
  449.             b = (XButtonInfoPtr) any;
  450.             __glutNumMouseButtons = b->num_buttons;
  451.           }
  452.           any = (XAnyClassPtr) ((char *) any + any->length);
  453.         }
  454.       }
  455.     skip_device:;
  456.     }
  457.     XFreeDeviceList(device_info);
  458.   }
  459. #else /* _WIN32 */
  460.   __glutNumMouseButtons = GetSystemMetrics(SM_CMOUSEBUTTONS);
  461. #endif /* !_WIN32 */
  462.   /* X Input extension might be supported, but only if there is
  463.      a tablet, dials, or spaceball do we claim devices are
  464.      supported. */
  465.   support = __glutTablet || __glutDials || __glutSpaceball;
  466.   return support;
  467. }
  468.  
  469. void
  470. __glutUpdateInputDeviceMask(GLUTwindow * window)
  471. {
  472. #if !defined(_WIN32)
  473.   /* 5 (dial and buttons) + 5 (tablet locator and buttons) + 5
  474.      (Spaceball buttons and axis) = 15 */
  475.   XEventClass eventList[15];
  476.   int rc, numEvents;
  477.  
  478.   rc = probeDevices();
  479.   if (rc) {
  480.     numEvents = 0;
  481.     if (__glutTablet) {
  482.       if (window->tabletMotion) {
  483.         DeviceMotionNotify(__glutTablet, __glutDeviceMotionNotify,
  484.           eventList[numEvents]);
  485.         numEvents++;
  486.       }
  487.       if (window->tabletButton) {
  488.         DeviceButtonPress(__glutTablet, __glutDeviceButtonPress,
  489.           eventList[numEvents]);
  490.         numEvents++;
  491.         DeviceButtonPressGrab(__glutTablet, __glutDeviceButtonPressGrab,
  492.           eventList[numEvents]);
  493.         numEvents++;
  494.         DeviceButtonRelease(__glutTablet, __glutDeviceButtonRelease,
  495.           eventList[numEvents]);
  496.         numEvents++;
  497.       }
  498.       if (window->tabletMotion || window->tabletButton) {
  499.         DeviceStateNotify(__glutTablet, __glutDeviceStateNotify,
  500.           eventList[numEvents]);
  501.         numEvents++;
  502.       }
  503.     }
  504.     if (__glutDials) {
  505.       if (window->dials) {
  506.         DeviceMotionNotify(__glutDials, __glutDeviceMotionNotify,
  507.           eventList[numEvents]);
  508.         numEvents++;
  509.       }
  510.       if (window->buttonBox) {
  511.         DeviceButtonPress(__glutDials, __glutDeviceButtonPress,
  512.           eventList[numEvents]);
  513.         numEvents++;
  514.         DeviceButtonPressGrab(__glutDials, __glutDeviceButtonPressGrab,
  515.           eventList[numEvents]);
  516.         numEvents++;
  517.         DeviceButtonRelease(__glutDials, __glutDeviceButtonRelease,
  518.           eventList[numEvents]);
  519.         numEvents++;
  520.       }
  521.       if (window->dials || window->buttonBox) {
  522.         DeviceStateNotify(__glutDials, __glutDeviceStateNotify,
  523.           eventList[numEvents]);
  524.         numEvents++;
  525.       }
  526.     }
  527.     if (__glutSpaceball) {
  528.       if (window->spaceMotion || window->spaceRotate) {
  529.         DeviceMotionNotify(__glutSpaceball, __glutDeviceMotionNotify,
  530.           eventList[numEvents]);
  531.         numEvents++;
  532.       }
  533.       if (window->spaceButton) {
  534.         DeviceButtonPress(__glutSpaceball, __glutDeviceButtonPress,
  535.           eventList[numEvents]);
  536.         numEvents++;
  537.         DeviceButtonPressGrab(__glutSpaceball, __glutDeviceButtonPressGrab,
  538.           eventList[numEvents]);
  539.         numEvents++;
  540.         DeviceButtonRelease(__glutSpaceball, __glutDeviceButtonRelease,
  541.           eventList[numEvents]);
  542.         numEvents++;
  543.       }
  544.       if (window->spaceMotion || window->spaceRotate || window->spaceButton) {
  545.         DeviceStateNotify(__glutSpaceball, __glutDeviceStateNotify,
  546.           eventList[numEvents]);
  547.         numEvents++;
  548.       }
  549.     }
  550. #if 0
  551.     if (window->children) {
  552.       GLUTwindow *child = window->children;
  553.  
  554.       do {
  555.         XChangeDeviceDontPropagateList(__glutDisplay, child->win,
  556.           numEvents, eventList, AddToList);
  557.         child = child->siblings;
  558.       } while (child);
  559.     }
  560. #endif
  561.     XSelectExtensionEvent(__glutDisplay, window->win,
  562.       eventList, numEvents);
  563.     if (window->overlay) {
  564.       XSelectExtensionEvent(__glutDisplay, window->overlay->win,
  565.         eventList, numEvents);
  566.     }
  567.   } else {
  568.     /* X Input extension not supported; no chance for exotic
  569.        input devices. */
  570.   }
  571. #endif /* !_WIN32 */
  572. }
  573.  
  574. /* CENTRY */
  575. int APIENTRY
  576. glutDeviceGet(GLenum param)
  577. {
  578.   probeDevices();
  579.   switch (param) {
  580.   case GLUT_HAS_KEYBOARD:
  581.   case GLUT_HAS_MOUSE:
  582.     /* Assume window system always has mouse and keyboard. */
  583.     return 1;
  584.   case GLUT_HAS_SPACEBALL:
  585.     return __glutSpaceball != NULL;
  586.   case GLUT_HAS_DIAL_AND_BUTTON_BOX:
  587.     return __glutDials != NULL;
  588.   case GLUT_HAS_TABLET:
  589.     return __glutTablet != NULL;
  590.   case GLUT_NUM_MOUSE_BUTTONS:
  591.     return __glutNumMouseButtons;
  592.   case GLUT_NUM_SPACEBALL_BUTTONS:
  593.     return __glutNumSpaceballButtons;
  594.   case GLUT_NUM_BUTTON_BOX_BUTTONS:
  595.     return __glutNumButtonBoxButtons;
  596.   case GLUT_NUM_DIALS:
  597.     return __glutNumDials;
  598.   case GLUT_NUM_TABLET_BUTTONS:
  599.     return __glutNumTabletButtons;
  600.   case GLUT_DEVICE_IGNORE_KEY_REPEAT:
  601.     return __glutCurrentWindow->ignoreKeyRepeat;
  602. #ifndef _WIN32
  603.   case GLUT_DEVICE_KEY_REPEAT:
  604.     {
  605.       XKeyboardState state;
  606.  
  607.       XGetKeyboardControl(__glutDisplay, &state);
  608.       return state.global_auto_repeat;
  609.     }
  610.   case GLUT_JOYSTICK_POLL_RATE:
  611.     return 0;
  612. #else
  613.   case GLUT_DEVICE_KEY_REPEAT:
  614.     /* Win32 cannot globally disable key repeat. */
  615.     return GLUT_KEY_REPEAT_ON;
  616.   case GLUT_JOYSTICK_POLL_RATE:
  617.     return __glutCurrentWindow->joyPollInterval;
  618. #endif
  619.   case GLUT_HAS_JOYSTICK:
  620.     return __glutHasJoystick;
  621.   case GLUT_JOYSTICK_BUTTONS:
  622.     return __glutNumJoystickButtons;
  623.   case GLUT_JOYSTICK_AXES:
  624.     return __glutNumJoystickAxes;
  625.   default:
  626.     __glutWarning("invalid glutDeviceGet parameter: %d", param);
  627.     return -1;
  628.   }
  629. }
  630. /* ENDCENTRY */
  631.